﻿// BookShelfService.cs
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.ServiceModel.DomainServices.EntityFramework;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using BookClub.Web.DataModel;
using BookClub.Web.WebServices;

namespace BookClub.Web.Services {

    // This service represents the current member's book shelf - the books they have shared out
    // with the book club.
    [EnableClientAccess]
    [RequiresAuthentication]
    public class BookShelfService : LinqToEntitiesDomainServiceEx<BookClubEntities> {

        private User _user;
        private IBookStoreService _bookStore;
        private INotificationService _notifier;

        public BookShelfService(User user, IBookStoreService bookStore, INotificationService notifier) {
            _user = user;
            _bookStore = bookStore;
            _notifier = notifier;
        }

        public override void Initialize(DomainServiceContext context) {
            base.Initialize(context);

            // This allows authorization rules to get access to the current user
            AuthorizationContext customAuthContext = new AuthorizationContext(context);
            customAuthContext.Items[typeof(User)] = _user;

            AuthorizationContext = customAuthContext;
        }

        // Gets the books shared out by the current user.
        public IQueryable<Book> GetBooks() {
            return ObjectContext.Books.Where(b => b.MemberID == _user.MemberID).
                                       OrderByDescending(b => b.AddedDate);
        }

        // Allows a member to share a book
        [Insert]
        public void ShareBook(Book book) {
            // Validate that the book doesn't already exist
            if (BookExists(book.ASIN)) {
                throw new ValidationException("The book already exists and cannot be added.");
            }

            // Validate that the book has a valid Amazon ID using the book store service
            if (_bookStore.IsValidASIN(book.ASIN) == false) {
                ValidationResult error = new ValidationResult("The specified Amazon ID did not resolve to an actual book.",
                                                              new string[] { "ASIN" });
                throw new ValidationException(error, null, book);
            }

            // Set any server controlled members, in this case the associated member id
            // and added date.
            book.MemberID = _user.MemberID;
            book.AddedDate = DateTime.UtcNow.Date;

            // Add the book to the database using the DAL
            ObjectContext.Books.AddObject(book);

            // Queue any work that needs to be done for this book, upon successful submit
            QueueOnSubmit(delegate() {
                // In particular using the notification service to send a new book notification
                _notifier.SendNewBookNotification(book);
            });
        }

        // Allow a book club member to update a book they previously shared.
        [CustomAuthorization(typeof(BookRules), "IsBookOwner")]
        public void UpdateBook(Book currentBook) {
            ObjectContext.Books.AttachAsModified(currentBook, ChangeSet.GetOriginal(currentBook));
        }

        // Allow a book club member to stop sharing a book they previously shared.
        [Delete]
        [CustomAuthorization(typeof(BookRules), "IsBookOwner")]
        public void UnshareBook(Book book) {
            if ((book.EntityState == EntityState.Detached)) {
                ObjectContext.Books.Attach(book);
            }
            ObjectContext.Books.DeleteObject(book);
        }

        public bool BookExists(string asin) {
            bool bookExists = false;

            using (BookClubEntities db = new BookClubEntities()) {
                bookExists = db.Books.Any(b => b.ASIN == asin);
            }
            return bookExists;
        }
    }
}
